/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BaseElement, element } from '../BaseElement';
import { getParseDateStr, toParseDate } from './LitDatePicker';
import { LitButton } from '../button/LitButton';

@element('lit-date-pane')
export class LitDatePane extends BaseElement {
  private datePaneEl: HTMLDivElement | undefined;
  private prevButtonEl: LitButton | undefined;
  private nextButtonEl: LitButton | undefined;
  private switchButtonEl: LitButton | undefined;
  private dateBodyEl: HTMLDivElement | undefined;
  private dateDayEl: HTMLDivElement | undefined;
  private dateMonthEl: HTMLDivElement | undefined;
  private dateYearEl: HTMLDivElement | undefined;
  private allDays: any;
  private allMonths: any;
  private allYears: any;
  private isInit: boolean = false;
  private isOkClick: boolean = false;
  private dateMode: string | undefined;
  private value: any;
  rangeDate: any;

  static get observedAttributes() {
    return ['minDate', 'maxDate', 'dateType'];
  }

  get defaultDateValue() {
    return this.getAttribute('defaultDateValue') || new Date();
  }

  get minDate(): any {
    let leftDate = this.getAttribute('minDate');
    return new Date(leftDate || '0-1-1').getTime();
  }

  get maxDate(): any {
    let rightDate = this.getAttribute('maxDate');
    if (rightDate) {
      return new Date(rightDate).getTime();
    }
    return new Date().getTime();
  }

  set defaultDateValue(defaultValue: any) {
    this.setAttribute('defaultValue', defaultValue);
  }

  setRangeDate(rangeValue: any) {
    this.rangeDate = rangeValue;
    this.renderDate();
  }

  get values() {
    return getParseDateStr(this.value, this.dateType);
  }

  get date() {
    return new Date(this.value);
  }

  get dateType() {
    return this.getAttribute('dateType') || 'date';
  }

  set minDate(minValue: string) {
    this.setAttribute('minDate', minValue);
  }

  set maxDate(maxValue: string) {
    this.setAttribute('maxDate', maxValue);
  }

  set dateType(typeValue: string) {
    this.setAttribute('dateType', typeValue);
  }

  get currentDateMode(): string {
    return this.dateMode || this.dateType;
  }

  set currentDateMode(currentValue: string) {
    this.dateMode = currentValue;
    this.dateDayEl!.dataset.type = currentValue;
  }

  set values(dateValue: any) {
    this.renderDate(dateValue);
    if (this.isInit && this.isOkClick) {
      this.isOkClick = !this.isOkClick;
    }
  }

  initElements(): void {
    this.datePaneEl = this.shadowRoot?.getElementById('date-content') as HTMLDivElement;
    this.prevButtonEl = this.datePaneEl!.querySelector('.prev-button') as LitButton;
    this.nextButtonEl = this.datePaneEl!.querySelector('.next-button') as LitButton;
    this.switchButtonEl = this.datePaneEl!.querySelector('.switch-button') as LitButton;
    this.dateBodyEl = this.datePaneEl!.querySelector('.day-body') as HTMLDivElement;
    this.dateDayEl = this.datePaneEl!.querySelector('.date-select') as HTMLDivElement;
    this.dateMonthEl = this.datePaneEl!.querySelector('.model-month') as HTMLDivElement;
    this.dateYearEl = this.datePaneEl!.querySelector('.model-year') as HTMLDivElement;
    this.allDays = this.dateBodyEl!.querySelectorAll<HTMLButtonElement>('button');
    this.allMonths = this.dateMonthEl!.querySelectorAll<HTMLButtonElement>('button');
    this.allYears = this.dateYearEl!.querySelectorAll<HTMLButtonElement>('button');
    this.currentDateMode = this.dateType;
    this.values = this.defaultDateValue;
    this.isInit = true;
  }

  connectedCallback() {
    this.prevButtonEl!.addEventListener('click', this.prevClickEvent);
    this.nextButtonEl!.addEventListener('click', this.nextClickEvent);
    this.switchButtonEl!.addEventListener('click', this.switchClickEvent);
    this.dateBodyEl!.addEventListener('click', this.dateBodyClickEvent);
    this.dateMonthEl!.addEventListener('click', this.dateMonthClickEvent);
    this.dateYearEl!.addEventListener('click', this.dateYearClickEvent);
  }

  initHtml(): string {
    return `<style>
            :host{
                display: block;
            }
            .content-head,.week{
                border: 0;
                display:flex;
            }
            .switch-button{
                border: 0;
                flex:1;
            }
            .switch-button[disabled]{
                border: 0;
                opacity:1;
            }
            .icon{
                width:1.1em;
                height:1.1em;
                fill: currentColor;
            }
            .prev-button,.next-button{
                width: 2.32em;
                height: 2.32em;
                transition: 0.32s;
                border: 0;
            }
            .prev-button[hidden],.next-button[hidden]{
                visibility: hidden;
                opacity: 0;
            }
            .switch-button{
                margin: 0 0.32em;
                border: 0;
            }
            .week-day{
                flex: 1;
                line-height: 2.42;
                text-align: center;
                border: 0;
            }
            .day-body{
                display: grid;
                grid-template-columns: repeat(7, 1fr);
                grid-template-rows: repeat(6, 1fr);
                font-family: PingFangSC-Regular;
                grid-gap: 0.52em;
                border: 0;
            }
            .div-button{
                position: relative;
                background: none;
                font-size: inherit;
                font-family: PingFangSC-Regular;
                border: 0;
                padding: 0;
                color: #333333;
                border-radius: 0.25em;
                transition: background 0.32s,color 0.32s,opacity 0.32s,border-color 0.32s,border-radius 0.32s;
                display: inline-flex;
                align-items: center;
                justify-content: center;
                border: 0;
                outline: 0;
            }
            .div-button::before{
                content: '';
                position: absolute; 
                background: #5B8FF9;
                border: 1px solid transparent;
                pointer-events: none; 
                left: 0; 
                right: 0; 
                top: 0; 
                bottom: 0; 
                font-family: PingFangSC-Regular;
                transition: 0.32s;  
                z-index: -2;
                border-radius: inherit;
                opacity: 0; 
            }
            .div-button:not([disabled]):not([current]):not([select]):not([startDate]):not([endDate]):hover,.div-button:not([disabled]):not([current]):not([select]):not([startDate]):not([endDate]):focus{
                color: #5B8FF9;
            }
            .div-button:not([disabled]):hover::before{
                opacity: 0.2 
            }
            .div-button:not([disabled]):focus::before{
                opacity: 0.2
            }
            .day-item{
                font-family: PingFangSC-Regular;
                box-sizing: content-box;
                min-width: 2.32em;
                height: 2.32em;
                justify-self: center;
            }
            .div-button[other]{
                opacity: 0.7;
                border: 0;
            }
            .div-button[disabled]{
                cursor: not-allowed;
                opacity: 0.7;
                background: rgba(0,0,0,0.1);
                border: 0;
            }
            .div-button[now]{
                color: #5B8FF9;
            }
            .div-button[current]{
                background: #5B8FF9;
                color: #ffffff;
            }
            .div-button[select]:not([other]){
                color: #ffffff;
                background: #5B8FF9;
            }
            .div-button[startDate]:not([other]),.div-button[endDate]:not([other]){
                color: #ffffff;
                border-color: #5B8FF9;
                background: #5B8FF9;
                border: 0;
            }
            .div-button[startDate]:not([other])::after,.div-button[endDate]:not([other])::after{
                content: ' ';
                width: 0;
                height: 0;
                top: 52%;
                overflow: hidden;
                border: 0.31em solid transparent;
                transform: translate(0, -52%);
                position: absolute;
            }
            .div-button[startDate]:not([other])::after{
                border-left-color: #5B8FF9;
                right: 100%;
            }
            .div-button[endDate]:not([other])::after{
                border-right-color: #5B8FF9;
                left: 100%;
            }
            .div-button[startDate][endDate]:not([other])::after{
                opacity: 0;
            }
            .div-button[disabled][current]{
                color: #ffffff;
            }
            .date-select{
                position: relative;
            }
            .model-month,.model-year{
                position: absolute;
                left: 0;
                top: 0.82em;
                right: 0;
                bottom: 0;
                grid-gap: 0.52em;
                display: grid;
            }
            .model-month{
                border: 0;
                grid-template-columns: repeat(3, 1fr);
                grid-template-rows: repeat(4, 1fr);
            }
            .model-year{
                grid-template-columns: repeat(4, 1fr);
                grid-template-rows: repeat(5, 1fr);
                border: 0;
            }
            .month-item,.year-item{
                display: flex;
                margin: auto;
                width: 100%;
                height: 100%;
                border: 0;
            }
            .select-mode{
                opacity: 0;
                visibility: hidden;
                z-index: -2;
                transition: 0.32s opacity,0.32s visibility;
            }
            :host([range]) .div-button[current]{
                background: transparent;
                color: #5B8FF9;
                border-color: #5B8FF9;
            }
            .date-select[data-type='date'] .mode-day,
            .date-select[data-type='month'] .model-month,
            .date-select[data-type='year'] .model-year{
                opacity: 1;
                visibility: visible;
                z-index: 1;
                border: 0;
            }
            
            .title-button {
                background: none;
                border: 0;
            }
            
            .title-button:hover {
                background: #EAF1FE;
                color: #5B8FF9;
                border-radius: 5px;
            }         
               
            .div-button {
                border: 0;
                background: none;
                color: #000000;
                border: 0;
            }
            
            .div-button:hover {
                background: #EAF1FE;
                color: #5B8FF9;
                border-radius: 5px;
            }
        </style>
        <div id='date-content'>
            <div class='content-head'>
                <button class='title-button prev-button'>
                       <lit-icon class='icon' name='prevbtn'></lit-icon>
                </button>
                <button class='title-button switch-button'></button>
                <button class='title-button next-button'>
                    <lit-icon class='icon' name='nextbtn'></lit-icon>
                </button>
            </div>
            <div class='date-select' data-type='date'>
                <div class='select-mode mode-day'>
                    <div class='week'>
                        <span class='week-day'>日</span>
                        <span class='week-day'>一</span>
                        <span class='week-day'>二</span>
                        <span class='week-day'>三</span>
                        <span class='week-day'>四</span>
                        <span class='week-day'>五</span>
                        <span class='week-day'>六</span>
                    </div>
                    <div class='day-body'>
                        ${Array.from({ length: 42 }, () => '<button class="div-button day-item"></button>').join('')}
                    </div>
                </div>
                <div class='select-mode model-month'>
                    ${this.getCurrentMonths()
        .map(
            (element, index) =>
                '<button class="div-button month-item" data-month="' +
                (index + 1).toString().padStart(2) +
                '">' +
                element +
                '</button>'
        )
        .join('')}
                </div>
                <div class='select-mode model-year'>
                    ${Array.from({ length: 20 }, () => '<button class="div-button year-item"></button>').join('')}
                </div>
            </div>
        </div>`;
  }

  getCurrentDays(currentYearDate: number, currentMonthDate: number) {
    let lastMonthDays = new Date(currentYearDate, currentMonthDate - 1, 0).getDate();
    let currentMonthDays = new Date(currentYearDate, currentMonthDate, 0).getDate();
    let startWeekDays = new Date(currentYearDate, currentMonthDate - 1, 1).getDay();
    let prevDay = Array.from(
        { length: startWeekDays },
        (element, index) =>
            (currentMonthDate == 1 ? currentYearDate - 1 : currentYearDate) +
            '-' +
            (currentMonthDate == 1 ? 12 : currentMonthDate - 1) +
            '-' +
            (lastMonthDays + index - startWeekDays + 1)
    );
    let currentDay = Array.from(
        { length: currentMonthDays },
        (element, index) => currentYearDate + '-' + currentMonthDate + '-' + (index + 1)
    );
    let nextDay = Array.from(
        { length: 42 - currentMonthDays - startWeekDays },
        (element, index) =>
            (currentMonthDate == 12 ? currentYearDate + 1 : currentYearDate) +
            '-' +
            (currentMonthDate == 12 ? 1 : currentMonthDate + 1) +
            '-' +
            (index + 1)
    );
    return [...prevDay, ...currentDay, ...nextDay];
  }

  getCurrentMonths() {
    return ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
  }

  getCurrentYears(yearDate: number) {
    let currentDate = (yearDate / 20) * 20;
    return Array.from({ length: 20 }, (yearElement, index) => currentDate + index);
  }

  toParseDay(yearDate: number, monthDate: number, dayDate: number) {
    let newDate = new Date(yearDate, monthDate + 1, 0).getDate();
    dayDate = dayDate > newDate ? newDate : dayDate;
    return [yearDate, monthDate, dayDate];
  }

  selectDate(value: any) {
    this.dispatchEvent(
        new CustomEvent('date-select', {
          detail: {
            date: this.date,
            value: value,
          },
        })
    );
  }

  renderDate(date: any = this.value) {
    this.value = date;
    let selectDate = toParseDate(date);
    switch (this.currentDateMode) {
      case 'date':
        let selectDays = this.getCurrentDays(selectDate[0], selectDate[1]);
        if (this.allDays) {
          this.allDays.forEach((dayElement: any, index: number) => {
            let selectDateSplit = selectDays[index].split('-');
            dayElement.dataset.date =
                selectDateSplit[0] +
                '-' +
                selectDateSplit[1].toString().padStart(2) +
                '-' +
                selectDateSplit[2].toString().padStart(2);
            dayElement.dataset.year = selectDateSplit[0];
            dayElement.dataset.month = selectDateSplit[1].toString().padStart(2);
            dayElement.dataset.day = selectDateSplit[2].toString().padStart(2);
            dayElement.textContent = selectDateSplit[2];
            if (selectDateSplit[1] != selectDate[1].toString()) {
              dayElement.setAttribute('other', '');
            } else {
              dayElement.removeAttribute('other');
            }
            let currentItemDate = new Date(dayElement.dataset.date.replace(/\s/g, '')).getTime();
            dayElement.disabled = currentItemDate < this.minDate || currentItemDate > this.maxDate;
            if (this.rangeDate) {
              if (
                  dayElement.dataset.date > getParseDateStr(this.rangeDate[0]) &&
                  dayElement.dataset.date < getParseDateStr(this.rangeDate[1])
              ) {
                dayElement.setAttribute('select', '');
              } else {
                dayElement.removeAttribute('select');
              }
              if (dayElement.dataset.date == getParseDateStr(this.rangeDate[0])) {
                dayElement.setAttribute('startDate', '');
              } else {
                dayElement.removeAttribute('startDate');
              }
              if (dayElement.dataset.date == getParseDateStr(this.rangeDate[1])) {
                dayElement.setAttribute('endDate', '');
              } else {
                dayElement.removeAttribute('endDate');
              }
            }
          });
          this.switchButtonEl!.textContent = selectDate[0] + '年' + (selectDate[1] + '').padStart(2) + '月';
        }
        break;
      case 'month':
        this.allMonths.forEach((monthElement: any) => {
          monthElement.dataset.date = selectDate[0] + '-' + monthElement.dataset.month;
          monthElement.dataset.year = selectDate[0];
          let currentItemDate = new Date(monthElement.dataset.date.replace(/\s/g, '')).getTime();
          monthElement.disabled = currentItemDate < this.minDate || currentItemDate > this.maxDate;
          if (this.rangeDate) {
            if (
                monthElement.dataset.date > getParseDateStr(this.rangeDate[0], 'month') &&
                monthElement.dataset.date < getParseDateStr(this.rangeDate[1], 'month')
            ) {
              monthElement.setAttribute('select', '');
            } else {
              monthElement.removeAttribute('select');
            }
            if (monthElement.dataset.date == getParseDateStr(this.rangeDate[0], 'month')) {
              monthElement.setAttribute('startDate', '');
            } else {
              monthElement.removeAttribute('startDate');
            }
            if (monthElement.dataset.date == getParseDateStr(this.rangeDate[1], 'month')) {
              monthElement.setAttribute('endDate', '');
            } else {
              monthElement.removeAttribute('endDate');
            }
          }
        });
        this.switchButtonEl!.textContent = selectDate[0] + '年';
        break;
      case 'year':
        let years = this.getCurrentYears(selectDate[0]);
        this.allYears.forEach((yearElement: any, index: number) => {
          yearElement.dataset.year = years[index];
          yearElement.dataset.date = years[index];
          yearElement.textContent = years[index];
          yearElement.disabled = false;
          let currentItemDate = new Date(yearElement.dataset.date.replace(/\s/g, '')).getTime();
          yearElement.disabled = currentItemDate < this.minDate || currentItemDate > this.maxDate;
          if (this.rangeDate) {
            if (
                yearElement.dataset.date > getParseDateStr(this.rangeDate[0], 'year') &&
                yearElement.dataset.date < getParseDateStr(this.rangeDate[1], 'year')
            ) {
              yearElement.setAttribute('select', '');
            } else {
              yearElement.removeAttribute('select');
            }
            if (yearElement.dataset.date == getParseDateStr(this.rangeDate[0], 'year')) {
              yearElement.setAttribute('startDate', '');
            } else {
              yearElement.removeAttribute('startDate');
            }
            if (yearElement.dataset.date == getParseDateStr(this.rangeDate[1], 'year')) {
              yearElement.setAttribute('endDate', '');
            } else {
              yearElement.removeAttribute('endDate');
            }
          }
        });
        this.switchButtonEl!.textContent = years[0] + '年 - ' + (years[0] + 19) + '年';
        break;
      default:
        break;
    }
  }

  dateYearClickEvent = (event: MouseEvent) => {
    // @ts-ignore
    let yearItem = event.target!.closest('button');
    let parseDate = toParseDate(this.value);
    this.isOkClick = true;
    if (yearItem) {
      switch (this.dateType) {
        case 'date':
          let day = new Date(yearItem.dataset.year, parseDate[1], 0).getDate();
          this.currentDateMode = 'month';
          this.values = yearItem.dataset.date + '-' + parseDate[1] + '-' + (parseDate[2] > day ? day : parseDate[2]);
          break;
        case 'month':
          this.currentDateMode = 'month';
          this.values = yearItem.dataset.date + '-' + parseDate[1];
          break;
        default:
          this.selectDate(yearItem.dataset.date + '');
          this.values = yearItem.dataset.date + '';
          break;
      }
    }
  };

  dateMonthClickEvent = (event: MouseEvent) => {
    // @ts-ignore
    let monthItem = event.target!.closest('button');
    let parseDate = toParseDate(this.value);
    this.isOkClick = true;
    if (monthItem) {
      if (this.dateType == 'date') {
        let day = new Date(parseDate[0], monthItem.dataset.month, 0).getDate();
        this.currentDateMode = 'date';
        this.values = monthItem.dataset.date + '-' + (parseDate[2] > day ? day : parseDate[2]);
      } else {
        this.selectDate(monthItem.dataset.date);
        this.values = monthItem.dataset.date;
      }
    }
  };

  dateBodyClickEvent = (event: MouseEvent) => {
    if (event!.target && event.target instanceof HTMLElement) {
      this.selectDate(event!.target.dataset.date);
      this.values = event!.target.dataset.date;
    }
  };

  switchClickEvent = (event: MouseEvent) => {
    switch (this.currentDateMode) {
      case 'date':
        this.currentDateMode = 'month';
        this.renderDate();
        break;
      case 'month':
        this.currentDateMode = 'year';
        this.renderDate();
        break;
      default:
        break;
    }
  };

  nextClickEvent = (event: MouseEvent) => {
    let parseDate = toParseDate(this.value);
    this.isOkClick = true;
    switch (this.currentDateMode) {
      case 'date':
        let dayNumbers = Object.values(this.toParseDay(parseDate[0], parseDate[1], parseDate[2]));
        this.values = new Date(dayNumbers[0], dayNumbers[1], dayNumbers[2]);
        break;
      case 'month':
        let mouthNumbers = Object.values(this.toParseDay(parseDate[0] + 1, parseDate[1] - 1, parseDate[2]));
        this.values = new Date(mouthNumbers[0], mouthNumbers[1], mouthNumbers[2]);
        break;
      case 'year':
        let yearNumbers = Object.values(this.toParseDay(parseDate[0] + 20, parseDate[1] - 1, parseDate[2]));
        this.values = new Date(yearNumbers[0], yearNumbers[1], yearNumbers[2]);
        break;
      default:
        break;
    }
  };

  prevClickEvent = (event: MouseEvent) => {
    let parseDate = toParseDate(this.value);
    this.isOkClick = true;
    switch (this.currentDateMode) {
      case 'date':
        let dayNumbers = Object.values(this.toParseDay(parseDate[0], parseDate[1] - 2, parseDate[2]));
        this.values = new Date(dayNumbers[0], dayNumbers[1], dayNumbers[2]);
        break;
      case 'month':
        let mouthNumbers = Object.values(this.toParseDay(parseDate[0] - 1, parseDate[1] - 1, parseDate[2]));
        this.values = new Date(mouthNumbers[0], mouthNumbers[1], mouthNumbers[2]);
        break;
      case 'year':
        let yearNumbers = Object.values(this.toParseDay(parseDate[0] - 20, parseDate[1] - 1, parseDate[2]));
        this.values = new Date(yearNumbers[0], yearNumbers[1], yearNumbers[2]);
        break;
      default:
        break;
    }
  };

  disconnectedCallback() {
    this.prevButtonEl!.removeEventListener('click', this.prevClickEvent);
    this.nextButtonEl!.removeEventListener('click', this.nextClickEvent);
    this.switchButtonEl!.removeEventListener('click', this.switchClickEvent);
    this.dateBodyEl!.removeEventListener('click', this.dateBodyClickEvent);
    this.dateMonthEl!.removeEventListener('click', this.dateMonthClickEvent);
    this.dateYearEl!.removeEventListener('click', this.dateYearClickEvent);
  }

  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
    if (!this.isInit || !this.datePaneEl || newValue == null) {
      return;
    }
    if (name === 'minDate' || name === 'maxDate' || name === 'dateType') {
      this.renderDate();
    }
  }
}
